#include "minesweeper.h"
#include "ui_minesweeper.h"

using namespace std;

MainGame::MainGame(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainGame)
{
    ui->setupUi(this);
}

MainGame::~MainGame()
{
    delete ui;
}

void MainGame::mainInterval()
{
    MainGame::update();
    QWidget::update();
}

void MainGame::clockCallback()
{
    if (status == PLAYING) { time += 1; }
}

void MainGame::setInterval()
{
    timer.interval.setInterval(1000 / GAME_FPS);
    timer.clock.setInterval(CLOCK_INTERVAL);
}

void MainGame::connectTimer()
{
    connect(&timer.interval, &QTimer::timeout, this, &MainGame::mainInterval);
    connect(&timer.clock, &QTimer::timeout, this, &MainGame::clockCallback);
}

void MainGame::startTimer()
{
    timer.interval.start();
    timer.clock.start();
}

void MainGame::loadImage()
{
    image.background.load(":/Images/background.png");
    image.block.load(":/Images/block.png");
    image.cover.load(":/Images/cover.png");
    image.error.load(":/Images/error.png");
    image.mine.load(":/Images/mine.png");
    image.mineError.load(":/Images/mine_error.png");
    image.flag.load(":/Images/flag.png");

    char path[TEXT_MAX_LEN];

    for (int i = 0; i < AROUND_MAX; i++)
    {
        sprintf(path, ":/Images/number_%d.png", i + 1);
        image.number[i].load(path);
    }
}

void MainGame::initGame()
{
    for (int i = 0; i < TABLE_LARGE; i++)
    {
        for (int j = 0; j < TABLE_LARGE; j++)
        {
            tableData[i][j].type = EMPTY;
            tableData[i][j].number = 0;
            tableData[i][j].isCovered = true;
            tableData[i][j].isMarked = false;
            tableData[i][j].isTouched = false;
            tableData[i][j].isError = false;
        }
    }
    status = PLAYING;
    flagCount = MINE_INIT_COUNT;
    time = 0;
    isWin = false;
    addMine();
    addNumber();
}

void MainGame::addMine()
{
    for (int i = 0; i < MINE_INIT_COUNT; i++)
    {
        int x = rand() % TABLE_LARGE;
        int y = rand() % TABLE_LARGE;

        if (tableData[x][y].type == MINE) { i--; }
        else
        {
            tableData[x][y].type = MINE;
            mineList[i] = { x, y };
        }
    }
}

void MainGame::addNumber()
{
    for (int i = 0; i < MINE_INIT_COUNT; i++)
    {
        for (int sideX = -1; sideX <= 1; sideX++)
        {
            for (int sideY = -1; sideY <= 1; sideY++)
            {
                int x = mineList[i].x + sideX;
                int y = mineList[i].y + sideY;

                if (x >= 0 && x < TABLE_LARGE && y >= 0 && y < TABLE_LARGE && tableData[x][y].type != MINE)
                {
                    tableData[x][y].type = NUMBER;
                    tableData[x][y].number += 1;
                }
            }
        }
    }
}

void MainGame::gameoverWin()
{
    int markCount = 0;
    bool isAllUncovered = true;

    for (int x = 0; x < TABLE_LARGE; x++)
    {
        for (int y = 0; y < TABLE_LARGE; y++)
        {
            if (tableData[x][y].type == MINE && tableData[x][y].isMarked) { markCount += 1; }
            else if (tableData[x][y].type != MINE && tableData[x][y].isCovered) { isAllUncovered = false; }
        }
    }
    if (markCount == MINE_INIT_COUNT && isAllUncovered)
    {
        status = OVER;
        isWin = true;
    }
}

void MainGame::gameoverLose(int x, int y)
{
    for (int x = 0; x < TABLE_LARGE; x++)
    {
        for (int y = 0; y < TABLE_LARGE; y++)
        {
            if (tableData[x][y].type == MINE && tableData[x][y].isMarked == false) { tableData[x][y].isCovered = false; }
            else if (tableData[x][y].type != MINE && tableData[x][y].isMarked == true) { tableData[x][y].isError = true; }
        }
    }
    tableData[x][y].isTouched = true;
    status = OVER;
}

void MainGame::update()
{
    int emptyCount = 0;

    for (int x = 0; x < TABLE_LARGE; x++)
    {
        for (int y = 0; y < TABLE_LARGE; y++)
        {
            if (tableData[x][y].type == EMPTY && !tableData[x][y].isCovered)
            {
                emptyList[emptyCount] = { x,y };
                emptyCount += 1;
            }
        }
    }
    for (int i = 0; i < emptyCount; i++)
    {
        for (int sideX = -1; sideX <= 1; sideX++)
        {
            for (int sideY = -1; sideY <= 1; sideY++)
            {
                int posX = emptyList[i].x + sideX;
                int posY = emptyList[i].y + sideY;

                if (posX >= 0 && posX < TABLE_LARGE && posY >= 0 && posY < TABLE_LARGE && !tableData[posX][posY].isMarked)
                {
                    tableData[posX][posY].isCovered = false;
                }
            }
        }
    }
}

void MainGame::mousePressEvent(QMouseEvent* event)
{
    int click = event->button();

    if (click)
    {
        if (status == PLAYING)
        {
            mouse = event->pos();

            if (mouse.x() >= BORDER_X && mouse.x() <= SCREEN_WIDTH - BORDER_X && mouse.y() >= BORDER_Y && mouse.y() <= SCREEN_HEIGHT - BORDER_X)
            {
                int x = ((mouse.x() - BORDER_X) / BLOCK_LARGE);
                int y = ((mouse.y() - BORDER_Y) / BLOCK_LARGE);

                if (click == Qt::LeftButton)
                {
                    if (tableData[x][y].isCovered && !tableData[x][y].isMarked)
                    {
                        if (tableData[x][y].type == MINE) { gameoverLose(x, y); }
                        tableData[x][y].isCovered = false;
                    }
                }
                else if (click == Qt::RightButton)
                {
                    if (!tableData[x][y].isMarked && tableData[x][y].isCovered)
                    {
                        tableData[x][y].isMarked = true;
                        flagCount -= 1;
                    }
                    else if (tableData[x][y].isMarked && tableData[x][y].isCovered)
                    {
                        tableData[x][y].isMarked = false;
                        flagCount += 1;
                    }
                }
            }
            gameoverWin();
        }
        else { initGame(); }
    }
}

void MainGame::displayBlock(QPainter& painter)
{
    for (int i = 0; i < TABLE_LARGE; i++)
    {
        for (int j = 0; j < TABLE_LARGE; j++)
        {
            int x = BORDER_X + i * BLOCK_LARGE;
            int y = BORDER_Y + j * BLOCK_LARGE;

            if (tableData[i][j].isCovered)
            {
                painter.drawPixmap(x, y, BLOCK_LARGE, BLOCK_LARGE, image.cover);

                if (tableData[i][j].isError)
                {
                    painter.drawPixmap(x, y, BLOCK_LARGE, BLOCK_LARGE, image.error);
                }
                else if (tableData[i][j].isMarked)
                {
                    painter.drawPixmap(x, y, BLOCK_LARGE, BLOCK_LARGE, image.flag);
                }
            }
            else
            {
                painter.drawPixmap(x, y, BLOCK_LARGE, BLOCK_LARGE, image.block);

                switch (tableData[i][j].type)
                {
                    case NUMBER: painter.drawPixmap(x, y, BLOCK_LARGE, BLOCK_LARGE, image.number[tableData[i][j].number - 1]); break;
                    case MINE: painter.drawPixmap(x, y, BLOCK_LARGE, BLOCK_LARGE, (tableData[i][j].isTouched) ? image.mineError : image.mine); break;
                }
            }
        }
    }
}

void MainGame::displayInfo(QPainter& painter)
{
    static char text[TEXT_MAX_LEN];

    sprintf(text, "TIME: %d", time);
    painter.drawText(BORDER_X, TEXT_POSITION, text);

    switch (status)
    {
        case PLAYING: sprintf(text, "MINES: %d", flagCount); break;
        case OVER: sprintf(text, (isWin) ? "Success!" : "BOOM!"); break;
    }
    painter.drawText(SCREEN_WIDTH - 120, TEXT_POSITION, text);
}

void MainGame::paintEvent(QPaintEvent*)
{
    QPainter painter(this);

    painter.drawPixmap(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, image.background);
    displayBlock(painter);
    displayInfo(painter);
}
